#ifdef _WIN32
#include <windows.h>
#endif

#include <string>
#include <iostream>
#include "glee/GLee.h"
#include <GL/glu.h>

#include "Key.h"
#include "targa.h"
#include "glslshader.h"
#include "AABBCollider.h"
#include "spherecollider.h"
#include "player.h"

using std::string;



const string KEY_TEXTURE = "data/textures/key.tga";

const string VERTEX_SHADER_120 = "data/shaders/glsl1.20/model.vert";
const string VERTEX_SHADER_130 = "data/shaders/glsl1.30/model.vert";

const string FRAGMENT_SHADER_120 = "data/shaders/glsl1.20/model.frag";
const string FRAGMENT_SHADER_130 = "data/shaders/glsl1.30/model.frag";

Key::Key(GameWorld* const world,int keyNumber):
Entity(world),
m_texID(0),
m_shaderProgram(NULL)
{
	m_keyNumber = keyNumber;
	m_width = 0.3f;
	m_height = 0.3f;
	m_depth = 0.3f;
	m_yaw = 0.0f;
	m_pitch = 0.0f;
    m_collider = new AABBCollider(this,m_width/2.0f,m_height/2.0f,m_depth/2.0f);
	setGravity(false);
	setVelocity(0.0f,0.0f,0.0f);
}


Key::~Key(void)
{
	delete m_collider;
	delete m_shaderProgram;
}


void Key::onPrepare(float dT)
{
	m_yaw += 4.0f * dT;
	m_pitch -= 3.0f + dT;

	if(m_yaw <= -360.0f) m_yaw += 360.0f;
	else if(m_yaw > 360.0f) m_yaw -= 360.0f;

	if(m_pitch <= -360.0f) m_pitch += 360.0f;
	else if(m_pitch > 360.0f) m_pitch -= 360.0f;
}

void Key::onRender() const
{
    static float modelviewMatrix[16];
    static float projectionMatrix[16];

    glPushMatrix();
	glTranslatef(m_position.x, m_position.y, m_position.z);
	glRotatef(m_pitch,1.0f,0.0f,0.0f);
	glRotatef(m_yaw,0.0f,1.0f,0.0);


    glGetFloatv(GL_MODELVIEW_MATRIX, modelviewMatrix);
    glGetFloatv(GL_PROJECTION_MATRIX, projectionMatrix);

    m_shaderProgram->bindShader();
    m_shaderProgram->sendUniform4x4("modelview_matrix", modelviewMatrix);
    m_shaderProgram->sendUniform4x4("projection_matrix", projectionMatrix);
	//m_shaderProgram->sendUniform("texture0", 0);

    glBindTexture(GL_TEXTURE_2D, m_texID);

    glEnableVertexAttribArray(0); //Enable the vertex attribute
   // glEnableVertexAttribArray(1); //Enable the color attribute
    glEnableVertexAttribArray(1); //Enable the texture coordinate attribute

	glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer);
    glVertexAttribPointer((GLint)0, 3, GL_FLOAT, GL_FALSE, 0, 0);	

	// SPECIFY VERTEX COLORS TO USE
	//glBindBuffer(GL_ARRAY_BUFFER, m_colorBuffer);
   // glVertexAttribPointer((GLint)1, 3, GL_FLOAT, GL_FALSE, 0, 0);

	// SPECIFY TEXTURING TO USE
	glBindBuffer(GL_ARRAY_BUFFER, m_texCoordBuffer);
    glVertexAttribPointer((GLint)1, 2, GL_FLOAT, GL_FALSE, 0, 0);

	// SPECIFY INDICES TO USE
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBuffer);

	// AND RENDER
 	glDrawElements(GL_TRIANGLES, indexBuffer.size(), GL_UNSIGNED_INT, 0);

	glDisableVertexAttribArray(1);
	//glDisableVertexAttribArray(1);
    glDisableVertexAttribArray(0);

    glPopMatrix();
}

void Key::onPostRender()
{

}

void Key::initializeVBOs()
{
	glGenBuffers(1, &m_vertexBuffer);
	glGenBuffers(1, &m_texCoordBuffer);
	glGenBuffers(1, &m_indexBuffer);
	glGenBuffers(1, &m_colorBuffer);

	generateVertices();
	generateIndices();
	generateVertexColors(); 
	generateTextureCoords();

	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBuffer);
    glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer);
    glVertexPointer(3, GL_FLOAT, 0, 0);

    glBindBuffer(GL_ARRAY_BUFFER, m_colorBuffer);
    glColorPointer(3, GL_FLOAT, 0, 0); 
}

void Key::generateVertices()
{
	float w2 = m_width / 2.0f;
	float h2 = m_height / 2.0f;
	float d2 = m_depth / 2.0f;
    vertexBuffer.push_back(Vertex(-w2,  -h2, d2)); //0
    vertexBuffer.push_back(Vertex(-w2,  -h2, -d2)); //1
    vertexBuffer.push_back(Vertex(w2,  -h2, -d2)); //2
    vertexBuffer.push_back(Vertex(w2,  -h2, d2)); //3
    vertexBuffer.push_back(Vertex(-w2, h2, d2)); //4
    vertexBuffer.push_back(Vertex(-w2, h2, -d2)); //5
    vertexBuffer.push_back(Vertex(w2,  h2, -d2)); //6
    vertexBuffer.push_back(Vertex(w2,  h2, d2)); //7

	

    vertexBuffer.push_back(Vertex(-w2,  h2, -d2));//8  5
    vertexBuffer.push_back(Vertex(w2,  h2, -d2));//9 6
	vertexBuffer.push_back(Vertex(-w2,  -h2, -d2));//10  1
    vertexBuffer.push_back(Vertex(w2,  -h2, -d2));//11  2


	// NOW SEND THE VERTEX DATA TO THE VBO ON THE GPU
    glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * vertexBuffer.size(), &vertexBuffer[0], GL_STATIC_DRAW);
}
void Key::generateIndices()
{
    
	indexBuffer.push_back(0);
    indexBuffer.push_back(11);//2);
    indexBuffer.push_back(3);
    indexBuffer.push_back(0);
    indexBuffer.push_back(10);//1);
    indexBuffer.push_back(11);//2);

    indexBuffer.push_back(4);
    indexBuffer.push_back(7);
    indexBuffer.push_back(9);//6);
    indexBuffer.push_back(4);
    indexBuffer.push_back(9);//6);
    indexBuffer.push_back(8);//5);

    indexBuffer.push_back(0);
    indexBuffer.push_back(4);
    indexBuffer.push_back(1);
    indexBuffer.push_back(4);
    indexBuffer.push_back(5);
    indexBuffer.push_back(1);

    indexBuffer.push_back(2);
    indexBuffer.push_back(6);
    indexBuffer.push_back(3);
    indexBuffer.push_back(6);
    indexBuffer.push_back(7);
    indexBuffer.push_back(3);

    indexBuffer.push_back(6);
    indexBuffer.push_back(1);
    indexBuffer.push_back(5);
    indexBuffer.push_back(6);
    indexBuffer.push_back(2);
    indexBuffer.push_back(1);

    indexBuffer.push_back(0);
    indexBuffer.push_back(7);
    indexBuffer.push_back(4);
    indexBuffer.push_back(0);
    indexBuffer.push_back(3);
    indexBuffer.push_back(7);
	
	// NOW SEND THE INDEX DATA TO THE VBO ON THE GPU
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBuffer);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * indexBuffer.size(), &indexBuffer[0], GL_STATIC_DRAW);
}

void Key::generateTextureCoords()
{
	m_texCoords.push_back(TexCoord(0.0f,0.0f));	// VERTEX 0
	m_texCoords.push_back(TexCoord(1.0f,0.0f));	// VERTEX 1
	m_texCoords.push_back(TexCoord(0.0f,0.0f));	// VERTEX 2
	m_texCoords.push_back(TexCoord(1.0f,0.0f));	// VERTEX 3
	m_texCoords.push_back(TexCoord(0.0f,1.0f));	// VERTEX 4
	m_texCoords.push_back(TexCoord(1.0f,1.0f));	// VERTEX 5
	m_texCoords.push_back(TexCoord(0.0f,1.0f));	// VERTEX 6
	m_texCoords.push_back(TexCoord(1.0f,1.0f));	// VERTEX 7
	m_texCoords.push_back(TexCoord(0.0f,0.0f));	// VERTEX 8
	m_texCoords.push_back(TexCoord(1.0f,0.0f));	// VERTEX 9
	m_texCoords.push_back(TexCoord(0.0f,1.0f));	// VERTEX 10
	m_texCoords.push_back(TexCoord(1.0f,1.0f));	// VERTEX 11
    glBindBuffer(GL_ARRAY_BUFFER, m_texCoordBuffer); //Bind the vertex buffer
    glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * m_texCoords.size() * 2, &m_texCoords[0], GL_STATIC_DRAW); //Send the data to OpenGL
}

void Key::generateVertexColors()
{
	for (int i = 0; i < vertexBuffer.size(); i++)
		colorBuffer.push_back(Color(1.0f, 1.0f, 1.0f, 1.0f));
 

    // SEND THE COLOR DATA TO THE VBO ON THE GPU
    glBindBuffer(GL_ARRAY_BUFFER, m_colorBuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 4 * colorBuffer.size(), &colorBuffer[0], GL_STATIC_DRAW);
}

void Key::addTriangle(Triangle& triangleToAdd)
{
	indexBuffer.push_back(triangleToAdd.v1);
	indexBuffer.push_back(triangleToAdd.v2);
	indexBuffer.push_back(triangleToAdd.v3);
}
bool Key::onInitialize()
{
    if(m_texID == 0)
    {
        TargaImage texture;
        if (!texture.load(KEY_TEXTURE))
        {
            std::cerr << "Couldn't load the Key texture" << std::endl;
            return false;
        }

        initializeVBOs();

        const string vertexShader = (GLSLProgram::glsl130Supported()) ? VERTEX_SHADER_130 : VERTEX_SHADER_120;
        const string fragmentShader = (GLSLProgram::glsl130Supported()) ? FRAGMENT_SHADER_130 : FRAGMENT_SHADER_120;

        m_shaderProgram = new GLSLProgram(vertexShader, fragmentShader);

        if (!m_shaderProgram->initialize())
        {
            std::cerr << "Could not initialize the Key shaders" << std::endl;
            return false;
        }
		m_shaderProgram->bindShader();
        m_shaderProgram->bindAttrib(0, "a_Vertex");
		//m_shaderProgram->bindAttrib(1, "a_Color");
		m_shaderProgram->bindAttrib(1, "a_TexCoord");
        m_shaderProgram->linkProgram();
		m_shaderProgram->sendUniform("texture0", 0);

        glGenTextures(1, &m_texID);
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, m_texID);

		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);

        gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB8, texture.getWidth(),
                      texture.getHeight(), GL_RGB, GL_UNSIGNED_BYTE,
                      texture.getImageData());
    }

    return true;

}

void Key::onShutdown()
{

}

void Key::onCollision(Entity* collider)
{


	destroy();
}